
#include "FreeRTOS.h"
#include "queue.h"
#include "semphr.h"
#include "task.h"

#include <stdint.h>
#include <stdio.h>

// #include <lwip/time.h>
#include <lwip/sockets.h>
// #include <lwip/select.h>
// #include "netdb.h"
// #include "iperf.h"

#include "lwip/opt.h"

#include "lwip/api.h"
#include "lwip/sys.h"

#define IPERF_PORT 5002
#define IPERF_BUFSZ (1 * 1024)

void iperf_server_socket(void *thread_param) {
    uint8_t *recv_data;
    socklen_t sin_size;
    uint32_t tick1;
    uint32_t tick2;
    int sock = -1;
    int connected;
    int bytes_received;
    uint64_t recvlen;
    struct sockaddr_in server_addr;
    struct sockaddr_in client_addr;
    fd_set readset;
    struct timeval timeout;
    lwip_socket_thread_init();

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        log_debug("Socket error\n");
        goto __exit;
    }

    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(IPERF_PORT);
    memset(&(server_addr.sin_zero), 0x0, sizeof(server_addr.sin_zero));

    if (bind(sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) ==
        -1) {
        log_debug("Unable to bind\n");
        goto __exit;
    }

    if (listen(sock, 5) == -1) {
        log_debug("Listen error\n");
        goto __exit;
    }

    timeout.tv_sec = 3;
    timeout.tv_usec = 0;

    log_debug("iperf_server at %d\n", IPERF_PORT);
    while (1) {
        FD_ZERO(&readset);
        FD_SET(sock, &readset);

        if (select(sock + 1, &readset, NULL, NULL, &timeout) == 0)
            continue;

        //        log_debug("iperf_server\n");

        sin_size = sizeof(struct sockaddr_in);

        connected = accept(sock, (struct sockaddr *) &client_addr, &sin_size);

        log_debug("new client connected from (%s, %d)\n",
                  inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
        recv_data = (uint8_t *) pvPortMalloc(IPERF_BUFSZ);
        if (recv_data == NULL) {
            log_debug("No memory\n");
            goto __exit;
        }

        {
            int flag = 1;

            setsockopt(connected, IPPROTO_TCP, /* set option at TCP level */
                       TCP_NODELAY,            /* name of option */
                       (void *) &flag,          /* the cast is historical cruft */
                       sizeof(int));           /* length of option value */
        }

        recvlen = 0;
        tick1 = HAL_GetTick();
        while (1) {
            bytes_received = recv(connected, recv_data, IPERF_BUFSZ, 0);
            if (bytes_received <= 0)
                break;
//            log_debug("recv %d", bytes_received);
            recvlen += bytes_received;

            tick2 = HAL_GetTick();
            if (tick2 - tick1 >= configTICK_RATE_HZ * 5) {
                int f;
                f = (float) (recvlen * configTICK_RATE_HZ / 125 / (tick2 - tick1));
                // f /= 1000.0f;
//                log_debug("%.4d Kbps!\n", f);
                tick1 = tick2;
                recvlen = 0;
            }
        }
        log_debug("recv exit.");
        free(recv_data);
        closesocket(connected);
        connected = -1;
    }

    __exit:
    if (sock >= 0)
        closesocket(sock);
    if (recv_data)
        free(recv_data);
}

void iperf_server_socket_init(void) {
    sys_thread_new("iperf_server", iperf_server_socket, NULL, 1024, 0);
}
